// *** LICENSE HEADER ***
// Filename: ./playerlist.cpp
// This file is part of Nodewarz 0.1
// Nodewarz is a 3d OpenGL strategy game
// Copyright (C) 2007 CZestmyr
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
// *** END LICENSE HEADER ***
#include "playerlist.h"

GLubyte playerList::defaultColors[8][3] = {
    {0, 0, 255}, //First player default color
    {255, 0, 0}, //Second player default color
    {0, 255, 0}, //etc...
    {255, 255, 0},
    {255, 0, 255},
    {0, 255, 255},
    {255, 255, 255},
    {128, 128, 255}
};

float playerList::defaultStartnodes[8][2] = {
    {-23.0f, -23.0f},
    {23.0f, 23.0f},
    {23.0f, -23.0f},
    {-23.0f, 23.0f},
    {0.0f, 15.0f},
    {15.0f, 0.0f},
    {-15.0f, 0.0f},
    {0.0f, -15.0f}
};

GLuint playerList::nodestackTexture = 0;

playerList::playerList(game* newParent) {
    parent = newParent;
    mode = MT_SELECT;
    nodeStack = new node*[16];
    nodeStackSize = 16;
    nodeStackPointer = 0;
    buffer[0] = 0;
    playerNum = 0;
    endGame = false;
}

playerList::~playerList() {
    DeletePlayers();
    delete[] nodeStack;
}

void playerList::SetConnector(connector* newConnector){
    theConnector = newConnector;
}

void playerList::LoadTextures(){
    nodestackTexture = ::LoadTexture("nodestack.png");
}

void playerList::UnloadTextures(){
    ::UnloadTexture(nodestackTexture);
}

void playerList::SetMode(modeType t){
    DeselectAllNodes();
    mode = t;

    //Changing modes erases node stack
    if (t != MT_ADD)
        nodeStackPointer = 0;

    //Set status bar messages
    switch (t) {
        case MT_SELECT: parent->GetStatusBar()->SetMessage("Click a node to get its info"); break;
        case MT_ADD_SELECT_CONNECTIONS: parent->GetStatusBar()->SetMessage("Select nodes to connect"); break;
        case MT_ADD: parent->GetStatusBar()->SetMessage("Node creation mode"); break;
        case MT_DISSINTEGRATE: parent->GetStatusBar()->SetMessage("Node dissintegration mode. Be careful!"); break;
    }
}

void playerList::DeletePlayers(){
    for (int i = 0; i < playerNum; ++i)
        delete players[i];
    playerNum = 0;
    activePlayer = -1;
    nodeStackPointer = 0;
}

void playerList::CreateDefaultPlayers(){
    DeletePlayers();
    round = 0;
    playerNum = 0;
    IncPlayers();
    activePlayer = 0;
}

void playerList::IncPlayers(){
    if (playerNum >= 8) return;

    //Create the player and set his data
    players[playerNum] = new player();
    players[playerNum]->ResetData();
    players[playerNum]->SetColor(defaultColors[playerNum][0], defaultColors[playerNum][1], defaultColors[playerNum][2]);
    players[playerNum]->SetLightManager(parent->GetLightManager());
    players[playerNum]->AddNode(defaultStartnodes[playerNum][0], defaultStartnodes[playerNum][1]);

    ++activePlayer;
    ++playerNum;
}

void playerList::DecPlayers(){
    if (playerNum <= 1) return;
    delete players[playerNum-1];
    nodeStackPointer = 0;
    --activePlayer;
    --playerNum;
}

bool playerList::NextPlayer(bool fromNet){
    //If the request came from the local client and the player is remote, return
    if (!fromNet && players[activePlayer]->GetType() == PT_REMOTE) return false;

    //Also if the game has ended, forbid round ending
    if (endGame){
        parent->GetStatusBar()->SetMessage("GAME HAS ENDED. Return to menu.");
        return false;
    }

    //First, deselect all nodes
    DeselectAllNodes();

    //Synchronize player data with other clients
    theConnector->PrepareEvent(_CONNECTOR_EVENT_PLAYERDATA);
    theConnector->AppendEventDataI(activePlayer);
    theConnector->AppendEventDataI(players[activePlayer]->GetCredits());
    theConnector->SendEvent();

    //Switch active player
    int prevPlayer = activePlayer;
    //Keeps switching, until finds a playing player
    do {
        activePlayer++;
        nodeStackPointer = 0;
        if (activePlayer >= playerNum) {
            activePlayer = 0;
            ++round;
        }
    } while (players[activePlayer]->HasLost());

    //If there are no other players left, end the game
    if (prevPlayer == activePlayer)
        endGame = true;

    //Update player's resources
    players[activePlayer]->ResetGrowthData();
    parent->GetResources()->Grow(this, activePlayer);

    //Update player
    players[activePlayer]->NewRound();
    parent->GetStatusBar()->SetMessage("");

    //Set the status bar message
    if (endGame)
        parent->GetStatusBar()->SetMessage("GAME HAS ENDED. Return to menu.");

    return !fromNet;
}

int playerList::SetPlayerNum(int playerNum){
    DeletePlayers();
    for (int i = 0; i < playerNum; ++i)
        IncPlayers();
}

int playerList::GetPlayerNum() {
    return playerNum;
}

int playerList::GetActivePlayer() {
    return activePlayer;
}

player* playerList::GetActivePlayerPointer() {
    return players[activePlayer];
}

player* playerList::GetPlayerPointer(int playerId) {
    Constrainti(playerId, 0, playerNum - 1);
    return players[playerId];
}

int playerList::GetRound() {
    return round;
}

void playerList::ConnectNodesInStack(){
    //Check whether exactly two nodes have been selected
    if (nodeStackPointer != 2){
        parent->GetStatusBar()->SetMessage("Select EXACTLY 2 nodes");
        return;
    }
    //Ensure that the connection doesn't exist yet
    if (nodeStack[0]->IsConnectedTo(nodeStack[1])){
        parent->GetStatusBar()->SetMessage("Connection already exists");
        return;
    }
    //Check whether the player has enough credits
    int cost = (int)(nodeStack[0]->CalculateDistanceTo(nodeStack[1]) *
                     nodeStack[0]->CalculateDistanceTo(nodeStack[1]) *
                     _CONNECTION_COST_MODIFIER);
    if (cost > players[activePlayer]->GetCredits()) {
        sprintf(buffer, "Need %i credits", cost);
        parent->GetStatusBar()->SetMessage(buffer);
        nodeStackPointer = 0;
        return;
    }
    //Finally connect the nodes
    players[activePlayer]->AddConnection(nodeStack[0],nodeStack[1]);
    //Subtract credits
    players[activePlayer]->SubtractCredits(cost);
    //Send info to other clients
    theConnector->PrepareEvent(_CONNECTOR_EVENT_CONNECTNODES);
    theConnector->AppendEventDataI(activePlayer);
    theConnector->AppendEventDataF(nodeStack[0]->GetX());
    theConnector->AppendEventDataF(nodeStack[0]->GetY());
    theConnector->AppendEventDataF(nodeStack[1]->GetX());
    theConnector->AppendEventDataF(nodeStack[1]->GetY());
    theConnector->SendEvent();
}

void playerList::DeselectAllNodes(){
    //Deselect all nodes
    for (int i = 0; i < playerNum; ++i)
        players[i]->DeselectSelected();
    //Reset gui labels
    if (parent->GetGui()){
        parent->GetGui()->SetElementText("N/A", _BUTTON_GAMELABEL1);
        parent->GetGui()->SetElementText("N/A", _BUTTON_GAMELABEL2);
        parent->GetGui()->SetElementText("N/A", _BUTTON_GAMELABEL3);
        parent->GetGui()->SetElementText("N/A", _BUTTON_GAMELABEL4);
        colorviewer* cv = (colorviewer*)parent->GetGui()->GetElement(_BUTTON_GAMENODECOLOR);
        if (cv)
            cv->SetColors(0, 0, 0);
    }
}

void playerList::Draw() {
    int i, cost;
    float x, y;
    bool showBoundaries = false;
    bool showEnergies = false;

    //Get showBoundaries and showEnergies bool values from the game GUI
    toggle* tg = (toggle*)parent->GetGui()->GetElement(_BUTTON_AREATOGGLE);
    if (tg)
        showBoundaries = tg->GetToggled();
    tg = (toggle*)parent->GetGui()->GetElement(_BUTTON_ENERGYTOGGLE);
    if (tg)
        showEnergies = tg->GetToggled();

    //Draw the players
    for (i = 0; i < playerNum; ++i) {
        if (i == activePlayer)
            players[i]->Draw(showBoundaries, true, showEnergies);
        else
            players[i]->Draw(showBoundaries, false, showEnergies);
    }
    if (mode == MT_ADD) {
        //Don't draw anything if outside the board
        if (parent->GetBoard()->IsInsideTheBoard(curX, curY)) {
            //Calculate potential addition cost
            cost = 10;
            for (i = 0; i < nodeStackPointer; ++i)
                cost += (int)(nodeStack[i]->CalculateDistanceTo(curX, curY) *
                              nodeStack[i]->CalculateDistanceTo(curX, curY) *
                              _CONNECTION_COST_MODIFIER);
            //Draw lines in place of future connections
            glColor3f(0.5f, 0.5f, 0.5f);
            for (i = 0; i < nodeStackPointer; ++i) {
                glDisable(GL_BLEND);
                glDisable(GL_TEXTURE_2D);
                glBegin(GL_LINES);
                glVertex3f(curX, curY, 0.06f);
                glVertex3f(nodeStack[i]->GetX(), nodeStack[i]->GetY(), 0.06f);
                glEnd();
            }
            glBegin(GL_LINES);
                glVertex3f(curX, curY, 0.12f);
                glVertex3f(curX, curY, 0.00f);
            glEnd();
            //Set status bar message
            sprintf(buffer, "Creation cost: %d", cost);
            parent->GetStatusBar()->SetMessage(buffer);
        }
    } else if (mode == MT_ADD_SELECT_CONNECTIONS) {
        if (nodeStackPointer == 2){
            cost = (int)(nodeStack[0]->CalculateDistanceTo(nodeStack[1]) *
                         nodeStack[0]->CalculateDistanceTo(nodeStack[1]) *
                         _CONNECTION_COST_MODIFIER);
            //Set status bar message
            sprintf(buffer, "Creation cost: %d", cost);
            parent->GetStatusBar()->SetMessage(buffer);
            //Draw line in place of future connection
            glColor3f(0.5f, 0.5f, 0.5f);
            glBegin(GL_LINES);
            glVertex3f(nodeStack[0]->GetX(), nodeStack[0]->GetY(), 0.06f);
            glVertex3f(nodeStack[1]->GetX(), nodeStack[1]->GetY(), 0.06f);
            glEnd();
        }
    } else if (mode == MT_ATTACK) {
        if (players[activePlayer]->GetSelectedNode() == 0)
            parent->GetStatusBar()->SetMessage("Select your attacking node!");
        else {
            //Display the probability of success in the status bar
            float probability = players[activePlayer]->GetSelectedNode()->CalculateProbabilityOfAttack(curX, curY);
            sprintf(buffer, "Probability of success: %.1f %%", probability*100.0f);
            parent->GetStatusBar()->SetMessage(buffer);
        }
    } else if (mode == MT_DEFEND) {
        if (players[activePlayer]->GetSelectedNode() == 0)
            parent->GetStatusBar()->SetMessage("Select your defending node!");
        else {
            //Display the probability of success in the status bar
            float probability = players[activePlayer]->GetSelectedNode()->CalculateProbabilityOfDefence(curX, curY);
            sprintf(buffer, "Probability of success: %.1f %%", probability*100.0f);
            parent->GetStatusBar()->SetMessage(buffer);
        }
    } else if (mode == MT_TAKEOVER) {
        if (players[activePlayer]->GetSelectedNode() == 0)
            parent->GetStatusBar()->SetMessage("Select your transferring node!");
        else {
            //Display the probability of success in the status bar
            float probability = players[activePlayer]->GetSelectedNode()->CalculateProbabilityOfTakeover(curX, curY);
            sprintf(buffer, "Probability of success: %.1f %%", probability*100.0f);
            parent->GetStatusBar()->SetMessage(buffer);
        }
    }

    //Display a message if other player is playing
    if (players[activePlayer]->GetType() == PT_REMOTE)
        parent->GetStatusBar()->SetMessage("PLEASE WAIT");

    //Draw the selected nodes stack
    glEnable(GL_BLEND);
    glBlendFunc(GL_ONE, GL_ONE);
    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, nodestackTexture);
    for (i = 0; i < nodeStackPointer; ++i) {
        x = nodeStack[i]->GetX();
        y = nodeStack[i]->GetY();
        glColor3f(0.5f, 0.5f, 0.5f);
        glBegin(GL_QUADS);
        glTexCoord2f(1.0f, 1.0f); glVertex3f(x + 0.5f, y + 0.5f, 0.05f);
        glTexCoord2f(0.0f, 1.0f); glVertex3f(x - 0.5f, y + 0.5f, 0.05f);
        glTexCoord2f(0.0f, 0.0f); glVertex3f(x - 0.5f, y - 0.5f, 0.05f);
        glTexCoord2f(1.0f, 0.0f); glVertex3f(x + 0.5f, y - 0.5f, 0.05f);
        glEnd();
    }
}

void playerList::SetPickingCursorPosition(float x, float y) {
    curX = x;
    curY = y;
}

void playerList::ClaimClientsPlayers(int clientNumber){
    for (int i = 0; i < playerNum; ++i){
        if (players[i]->GetType() == PT_REMOTE && players[i]->GetClientNumber() == clientNumber)
            players[i]->SetType(PT_LOCAL);
    }
}

bool playerList::IsEndGame(){
    return endGame;
}

void playerList::MouseClickEvent(int xCoordinate, int yCoordinate, bool right) {
    //Helper variables (cannot declare them inside the case construct)
    float x, y, probability;
    float closestNodes[8];
    int i, playerWithClosestNode, damage, cost;
    node* helper;
    node* helper2;
    bool flag;

    //Update the picking cursor coordinates
    PickAtZPlane(x, y, xCoordinate, yCoordinate);

    //Don't react if other players are playing
    if (players[activePlayer]->GetType() == PT_REMOTE) return;

    switch (mode) {
        case MT_SELECT:
            //Calculate distances to the closest nodes for each player
            for (i = 0; i < playerNum; ++i) {
                helper = players[i]->GetClosestNode(x, y);
                if (helper)
                    closestNodes[i] = helper->CalculateDistanceTo(x, y);
                else
                    closestNodes[i] = FLT_MAX;
            }
            //Find the player with the closest node of all
            for (i = 0; i < playerNum; ++i) {
                if (i == 0 || closestNodes[i] < closestNodes[playerWithClosestNode])
                    playerWithClosestNode = i;
            }
            //Deselect any previously selected nodes
            DeselectAllNodes();
            //Select the closest node
            players[playerWithClosestNode]->SelectNode(x, y);
            SetInfoLabelsForClosestNode(players[playerWithClosestNode]);
            parent->GetStatusBar()->SetMessage("");
            break;
        case MT_ADD_SELECT_CONNECTIONS:
            helper = players[activePlayer]->GetClosestNode(x, y);
            if (helper && !NodeIsInStack(helper))
                AddNodeToStack(helper);
            break;
        case MT_ADD:
            //Check whether we are too close to our or enemy nodes
            flag = false;
            for (i = 0; i < playerNum; ++i){
                helper = players[i]->GetClosestNode(x, y);
                if (!helper) continue;
                if (i != activePlayer && helper->CalculateDistanceTo(x,y) <= _MINDIST_ENEMY){
                    parent->GetStatusBar()->SetMessage("Too close to enemy");
                    flag = true;
                    break;
                }
                else if (i == activePlayer && helper->CalculateDistanceTo(x,y) <= _MINDIST_FRIENDLY){
                    parent->GetStatusBar()->SetMessage("Too close to other nodes");
                    flag = true;
                    break;
                }
            }
            if (flag) break;
            //Prevent the player from creating unconnected nodes
            if (nodeStackPointer == 0) {
                parent->GetStatusBar()->SetMessage("Cannot create unconnected node");
                mode = MT_SELECT;
                break;
            }
            //Calculate cost of construction
            cost = 10; //New node cost
            for (i = 0; i < nodeStackPointer; ++i)
                cost += (int)(nodeStack[i]->CalculateDistanceTo(x, y) *
                              nodeStack[i]->CalculateDistanceTo(x, y) *
                              _CONNECTION_COST_MODIFIER);
            //Prevent from constructing without having enough money
            if (cost > players[activePlayer]->GetCredits()) {
                parent->GetStatusBar()->SetMessage("Not enough credits");
                nodeStackPointer = 0;
                mode = MT_SELECT;
                break;
            }
            //Prevent from constructing outside the game board
            if (!parent->GetBoard()->IsInsideTheBoard(x, y)){
                parent->GetStatusBar()->SetMessage("Must be on the board");
                break;
            }
            //Create new node
            helper = players[activePlayer]->AddNode(x, y);
            //Subtract the cost from credits
            players[activePlayer]->SubtractCredits(cost);
            //Add connections
            for (i = 0; i < nodeStackPointer; ++i)
                players[activePlayer]->AddConnection(helper, nodeStack[i]);
            //Send events via network
            theConnector->PrepareEvent(_CONNECTOR_EVENT_CREATENODE);
            theConnector->AppendEventDataI(activePlayer);
            theConnector->AppendEventDataF(x);
            theConnector->AppendEventDataF(y);
            theConnector->SendEvent();
            for (i = 0; i < nodeStackPointer; ++i) {
                theConnector->PrepareEvent(_CONNECTOR_EVENT_CONNECTNODES);
                theConnector->AppendEventDataI(activePlayer);
                theConnector->AppendEventDataF(x);
                theConnector->AppendEventDataF(y);
                theConnector->AppendEventDataF(nodeStack[i]->GetX());
                theConnector->AppendEventDataF(nodeStack[i]->GetY());
                theConnector->SendEvent();
            }
            //Clean node stack
            nodeStackPointer = 0;
            parent->GetStatusBar()->SetMessage("Created a new node");
            mode = MT_SELECT;
            break;
        case MT_ATTACK:
            //Check, whether we have enough credits for attack
            if (players[activePlayer]->GetCredits() < 3) {
                parent->GetStatusBar()->SetMessage("Not enough credits");
                break;
            }
            //Player hasn't selected a node to attack
            if (players[activePlayer]->GetSelectedNode() == 0) {
                players[activePlayer]->SelectNode(x, y);
                SetInfoLabelsForClosestNode(players[activePlayer]);
                break;
            }
            //Calculate distances to the closest nodes for each player
            for (i = 0; i < playerNum; ++i) {
                if (i == activePlayer) continue;
                helper = players[i]->GetClosestNode(x, y);
                if (helper)
                    closestNodes[i] = helper->CalculateDistanceTo(x, y);
                else
                    closestNodes[i] = FLT_MAX;
            }
            //Find the player with the closest node of all
            playerWithClosestNode = playerNum;
            for (i = 0; i < playerNum; ++i) {
                if (i == activePlayer) continue;
                if (playerWithClosestNode == playerNum ||
                        closestNodes[i] < closestNodes[playerWithClosestNode])
                    playerWithClosestNode = i;
            }
            //We can't select any node to attack
            if (playerWithClosestNode == playerNum) {
                players[activePlayer]->DeselectSelected();
                parent->GetStatusBar()->SetMessage("No enemy nodes to attack!");
                break;
            }
            //Select the closest node
            helper = players[playerWithClosestNode]->GetClosestNode(x, y);
            if (!helper) {
                players[activePlayer]->DeselectSelected();
                parent->GetStatusBar()->SetMessage("No enemy nodes to attack!");
                break;
            }

            //Calculate the probability of a successful attack based on the node distances
            probability = players[activePlayer]->GetSelectedNode()->CalculateProbabilityOfAttack(helper);
            //Decrease credit number
            players[activePlayer]->SubtractCredits(3);
            //If the attack was a success
            if ((rand() % 1000) / 1000.0f <= probability) {
                parent->GetParticleSystem()->CreateEmitter(helper->GetX(), helper->GetY(), 0.0f, 0, _PET_FIRE);
                if (helper->Damage(players[activePlayer]->GetSelectedNode()->GetAttackEnergy())) {
                    parent->GetStatusBar()->SetMessage("Enemy node destroyed!");
                }
                parent->GetStatusBar()->SetMessage("Attack successful!");
                //Attack was successful. Inform other clients
                theConnector->PrepareEvent(_CONNECTOR_EVENT_ATTACK);
                theConnector->AppendEventDataI(activePlayer);
                theConnector->AppendEventDataF(players[activePlayer]->GetSelectedNode()->GetX());
                theConnector->AppendEventDataF(players[activePlayer]->GetSelectedNode()->GetY());
                theConnector->AppendEventDataI(playerWithClosestNode);
                theConnector->AppendEventDataF(helper->GetX());
                theConnector->AppendEventDataF(helper->GetY());
                theConnector->SendEvent();
            } else {
                parent->GetStatusBar()->SetMessage("Attack unsuccessful!");
                //Inform others
                theConnector->PrepareEvent(_CONNECTOR_EVENT_IMPATTACK);
                theConnector->AppendEventDataI(activePlayer);
                theConnector->AppendEventDataF(players[activePlayer]->GetSelectedNode()->GetX());
                theConnector->AppendEventDataF(players[activePlayer]->GetSelectedNode()->GetY());
                theConnector->SendEvent();
            }
            //Improve the node's attacking abilities
            players[activePlayer]->GetSelectedNode()->ImproveAttack();
            players[activePlayer]->DeselectSelected();

            mode = MT_SELECT;
            break;
        case MT_DEFEND:
            //Check, whether we have enough credits for defending
            if (players[activePlayer]->GetCredits() < 5) {
                parent->GetStatusBar()->SetMessage("Not enough credits");
                break;
            }
            //Player hasn't selected a node that should defend
            if (players[activePlayer]->GetSelectedNode() == 0) {
                players[activePlayer]->SelectNode(x, y);
                SetInfoLabelsForClosestNode(players[activePlayer]);
                break;
            }
            //Find the active player's node that is closest to the click
            helper = players[activePlayer]->GetClosestNode(x, y);
            //Prevent self-healing
            if (helper == players[activePlayer]->GetSelectedNode()) {
                parent->GetStatusBar()->SetMessage("Node cannot heal itself");
                break;
            }
            //Calculate the probability of a successful defend based on the node distances
            probability = players[activePlayer]->GetSelectedNode()->CalculateProbabilityOfDefence(helper);

            //Decrease credit number
            players[activePlayer]->SubtractCredits(5);
            //If the defence was a success
            if ((rand() % 1000) / 1000.0f <= probability) {
                parent->GetParticleSystem()->CreateEmitter(helper->GetX(), helper->GetY(), 0.0f, 1, _PET_POWERSPHERE);
                helper->Defend(players[activePlayer]->GetSelectedNode()->GetDefenceEnergy());
                parent->GetStatusBar()->SetMessage("Defence successful!");
                //Defence was successful. Inform other clients
                theConnector->PrepareEvent(_CONNECTOR_EVENT_DEFEND);
                theConnector->AppendEventDataI(activePlayer);
                theConnector->AppendEventDataF(players[activePlayer]->GetSelectedNode()->GetX());
                theConnector->AppendEventDataF(players[activePlayer]->GetSelectedNode()->GetY());
                theConnector->AppendEventDataF(helper->GetX());
                theConnector->AppendEventDataF(helper->GetY());
                theConnector->SendEvent();
            } else {
                parent->GetStatusBar()->SetMessage("Defence unsuccessful!");
                //Inform others
                theConnector->PrepareEvent(_CONNECTOR_EVENT_IMPDEFENCE);
                theConnector->AppendEventDataI(activePlayer);
                theConnector->AppendEventDataF(players[activePlayer]->GetSelectedNode()->GetX());
                theConnector->AppendEventDataF(players[activePlayer]->GetSelectedNode()->GetY());
                theConnector->SendEvent();
            }
            //Improve the node's attacking abilities
            players[activePlayer]->GetSelectedNode()->ImproveDefence();
            players[activePlayer]->DeselectSelected();

            mode = MT_SELECT;
            break;
        case MT_TAKEOVER:
            //Check, whether we have enough credits for takeover
            if (players[activePlayer]->GetCredits() < 5) {
                parent->GetStatusBar()->SetMessage("Not enough credits");
                break;
            }
            //Player hasn't selected a node that will perform the takeover
            if (players[activePlayer]->GetSelectedNode() == 0) {
                players[activePlayer]->SelectNode(x, y);
                SetInfoLabelsForClosestNode(players[activePlayer]);
                break;
            }
            //Calculate distances to the closest nodes for each player
            for (i = 0; i < playerNum; ++i) {
                helper = players[i]->GetClosestNode(x, y);
                if (helper)
                    closestNodes[i] = helper->CalculateDistanceTo(x, y);
                else
                    closestNodes[i] = FLT_MAX;
            }
            //Find the player with the closest node of all
            playerWithClosestNode = playerNum;
            for (i = 0; i < playerNum; ++i) {
                if (playerWithClosestNode == playerNum ||
                        closestNodes[i] < closestNodes[playerWithClosestNode])
                    playerWithClosestNode = i;
            }
            //Select the closest node of all
            helper = players[playerWithClosestNode]->GetClosestNode(x, y);
            //Prevent self-takeovers
            if (helper == players[activePlayer]->GetSelectedNode()){
                parent->GetStatusBar()->SetMessage("Cannot psi on self");
                break;
            }
            //Calculate the probability of a successful psi burst based on the node distances
            probability = players[activePlayer]->GetSelectedNode()->CalculateProbabilityOfTakeover(helper);

            //Decrease credit number
            players[activePlayer]->SubtractCredits(5);
            //If the takeover was a success
            if ((rand() % 1000) / 1000.0f <= probability) {
                //Distinguish between psi strengthening and takeover attacks
                if (players[activePlayer]->GetSelectedNode()->IsFriendly(helper)){
                    helper->FriendlyTakeover(players[activePlayer]->GetSelectedNode()->GetTakeoverEnergy());
                    parent->GetStatusBar()->SetMessage("Psi burst successful");
                    parent->GetParticleSystem()->CreateEmitter(players[activePlayer]->GetSelectedNode()->GetX(), players[activePlayer]->GetSelectedNode()->GetY(), 0.0f, 1, _PET_PSIBURST, helper->GetX(), helper->GetY());
                    //Inform other clients
                    theConnector->PrepareEvent(_CONNECTOR_EVENT_TAKEOVERDEFENCE);
                    theConnector->AppendEventDataI(activePlayer);
                    theConnector->AppendEventDataF(players[activePlayer]->GetSelectedNode()->GetX());
                    theConnector->AppendEventDataF(players[activePlayer]->GetSelectedNode()->GetY());
                    theConnector->AppendEventDataF(helper->GetX());
                    theConnector->AppendEventDataF(helper->GetY());
                    theConnector->SendEvent();
                } else {
                    parent->GetParticleSystem()->CreateEmitter(helper->GetX(), helper->GetY(), 0.0f, 1, _PET_PSIBURST, players[activePlayer]->GetSelectedNode()->GetX(), players[activePlayer]->GetSelectedNode()->GetY());
                    //Inform other clients
                    theConnector->PrepareEvent(_CONNECTOR_EVENT_TAKEOVERATTACK);
                    theConnector->AppendEventDataI(activePlayer);
                    theConnector->AppendEventDataF(players[activePlayer]->GetSelectedNode()->GetX());
                    theConnector->AppendEventDataF(players[activePlayer]->GetSelectedNode()->GetY());
                    theConnector->AppendEventDataI(playerWithClosestNode);
                    theConnector->AppendEventDataF(helper->GetX());
                    theConnector->AppendEventDataF(helper->GetY());
                    theConnector->SendEvent();
                    if (helper->EnemyTakeover(players[activePlayer]->GetSelectedNode()->GetTakeoverEnergy())){
                        //Node was taken over by you
                        helper2 = players[activePlayer]->AddNode(helper->GetX(), helper->GetY());
                        helper2->CopyAtributesFrom(helper);
                        helper2->SetTakeoverEnergy(20);
                        players[playerWithClosestNode]->RemoveNode(helper);
                    }
                    parent->GetStatusBar()->SetMessage("Psi attack successful");
                }
            } else {
                parent->GetStatusBar()->SetMessage("Psi burst unsuccessful");
                //Inform other clients
                theConnector->PrepareEvent(_CONNECTOR_EVENT_IMPTAKEOVER);
                theConnector->AppendEventDataI(activePlayer);
                theConnector->AppendEventDataF(players[activePlayer]->GetSelectedNode()->GetX());
                theConnector->AppendEventDataF(players[activePlayer]->GetSelectedNode()->GetY());
                theConnector->SendEvent();
            }
            //Improve takeover skill
            players[activePlayer]->GetSelectedNode()->ImproveTakeover();

            SetMode(MT_SELECT);
            break;
        case MT_DISSINTEGRATE:
            //Need to click twice just to make sure
            if (players[activePlayer]->GetSelectedNode() == 0 || players[activePlayer]->GetClosestNode(x, y) != players[activePlayer]->GetSelectedNode()) {
                players[activePlayer]->SelectNode(x, y);
                SetInfoLabelsForClosestNode(players[activePlayer]);
                parent->GetStatusBar()->SetMessage("Click this node again to dissintegrate it");
                break;
            }
            helper = players[activePlayer]->GetSelectedNode();
            players[activePlayer]->DeselectSelected();

            parent->GetParticleSystem()->CreateEmitter(helper->GetX(), helper->GetY(), 0.0f, 0, _PET_SMOKE);

            //Inform the other clients
            theConnector->PrepareEvent(_CONNECTOR_EVENT_DISSINTEGRATE);
            theConnector->AppendEventDataI(activePlayer);
            theConnector->AppendEventDataF(helper->GetX());
            theConnector->AppendEventDataF(helper->GetY());
            theConnector->SendEvent();

            players[activePlayer]->RemoveNode(helper);
            players[activePlayer]->SubtractCredits(5);

            SetMode(MT_SELECT);
            break;
    }
}

void playerList::MouseReleaseEvent(int xCoordinate, int yCoordinate, bool right) {}

void playerList::MouseMoveEvent(int newX, int newY, int relX, int relY) {}

void playerList::KeyPressEvent(SDLKey key, char c) {
    if (players[activePlayer]->GetType() == PT_REMOTE) return;

    switch (key) {
        case SDLK_F2:
        case SDLK_ESCAPE: SetMode(MT_SELECT);
            break;
        case SDLK_F4: SetMode(MT_ADD);
            break;
        case SDLK_F3: SetMode(MT_ADD_SELECT_CONNECTIONS);
            break;
        case SDLK_F5: SetMode(MT_ATTACK);
            break;
        case SDLK_F6: SetMode(MT_DEFEND);
            break;
        case SDLK_F7: SetMode(MT_TAKEOVER);
            break;
        case SDLK_F10: SetMode(MT_DISSINTEGRATE);
            break;
    }
}

void playerList::KeyReleaseEvent(SDLKey key) {}

void playerList::ReallocateNodeStack() {
    node** newStack = new node*[nodeStackSize * 2];
    memcpy(newStack, nodeStack, nodeStackSize*sizeof(node*));
    delete[] nodeStack;
    nodeStack = newStack;
    nodeStackSize *= 2;
}

void playerList::AddNodeToStack(node* n) {
    if (!NodeIsInStack(n)) {
        if (nodeStackPointer == nodeStackSize)
            ReallocateNodeStack();
        nodeStack[nodeStackPointer] = n;
        ++nodeStackPointer;
    }
}

bool playerList::NodeIsInStack(node* n) {
    bool ret = false;
    for (int i = 0; i < nodeStackPointer; ++i)
        if (nodeStack[i] == n)
            ret = true;
    return ret;
}

void playerList::SetInfoLabelsForClosestNode(player* pl){
    node* nd = pl->GetSelectedNode();
    if (nd) {
        char buffer[8];
        sprintf(buffer, "%i", nd->GetThetaEnergy());
        parent->GetGui()->SetElementText(buffer, _BUTTON_GAMELABEL1);
        sprintf(buffer, "%i", nd->GetAttackEnergy());
        parent->GetGui()->SetElementText(buffer, _BUTTON_GAMELABEL2);
        sprintf(buffer, "%i", nd->GetDefenceEnergy());
        parent->GetGui()->SetElementText(buffer, _BUTTON_GAMELABEL3);
        sprintf(buffer, "%i", nd->GetTakeoverEnergy());
        parent->GetGui()->SetElementText(buffer, _BUTTON_GAMELABEL4);
    }
    SDL_Color c = pl->GetColor();
    ((colorviewer*)parent->GetGui()->GetElement(_BUTTON_GAMENODECOLOR))->SetColors(c.r, c.g, c.b);
}
